﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Diagnostics;

namespace System.Windows.Media.DirectShow.Filters
{
    internal class DeviceCaptureSourceAudioPin : BaseOutputPin
    {

        private AMMediaType mediaType;
        private DeviceCaptureSource filter;
        private SLAudioSink sink;
        private WaveFormatExtensible format;

        public DeviceCaptureSourceAudioPin(DeviceCaptureSource filter)
            : base(filter)
        {
            this.filter = filter;

            this.mediaType = new AMMediaType();
            this.mediaType.MajorType = MediaMajorTypes.Audio;

            this.sink = new SLAudioSink(this);
        }

        public override Collections.Generic.IEnumerable<AMMediaType> MediaTypes
        {
            get { return new AMMediaType[] { this.mediaType }; }
        }

        public override string Name
        {
            get { return "Device Capture Source Audio"; }
        }

        internal void PreparePin()
        {
            this.sink.InitMode = true;
            this.sink.CaptureSource = filter.CaptureSource;
        }

        public override void Run(TimeSpan start)
        {
            base.Run(start);

            this.sink.InitMode = false;
        }

        protected void SetAudioFormat(AudioFormat audioFormat)
        {
            if (this.Filter.State != FilterState.Stopped)
                throw new InvalidOperationException("Cannot change audio format while filter is running");

            AMMediaType mediaType = new AMMediaType
            {
                FixedSizeSamples = true,
                MajorType = MediaMajorTypes.Audio,
                SubType = MediaSubTypes.PCM,
                SampleSize = audioFormat.SamplesPerSecond,
                Format = format = new WaveFormatExtensible
                {
                    AverageBytesPerSecond = audioFormat.SamplesPerSecond * 2 * audioFormat.Channels,
                    BitsPerSample = (short)audioFormat.BitsPerSample,
                    Channels = (short)audioFormat.Channels,
                    BlockAlign = (short)(audioFormat.BitsPerSample * audioFormat.Channels / 8),
                    SamplesPerSec = audioFormat.SamplesPerSecond,
                    FormatTag = 1,
                }
            };

            this.mediaType = mediaType;
            this.filter.PinReady(this);
        }

        protected internal override void DeliverAsync(IMediaSample sample)
        {
            if (sample.StartMediaTime >= 0)
            {
                TimeSpan start = TimeSpan.FromSeconds(sample.StartMediaTime / (double)format.AverageBytesPerSecond);
                TimeSpan end = TimeSpan.FromSeconds(sample.EndMediaTime / (double)format.AverageBytesPerSecond);

                Debug.WriteLine(start);

                // Set sample parameters
                sample.StartTime = start;
                sample.EndTime = end;
                sample.MediaType = mediaType;

                base.DeliverAsync(sample);
            }
        }

        protected class SLAudioSink : AudioSink
        {

            private DeviceCaptureSourceAudioPin source;
            private long startOffset;

            public bool InitMode { get; set; }

            public SLAudioSink(DeviceCaptureSourceAudioPin source)
            {
                this.source = source;
            }

            protected override void OnCaptureStarted()
            {
                startOffset = 0;
            }

            protected override void OnCaptureStopped()
            {
            }

            protected override void OnFormatChange(AudioFormat audioFormat)
            {
                if (this.InitMode)
                    this.source.SetAudioFormat(audioFormat);
            }

            protected override void OnSamples(long sampleTime, long sampleDuration, byte[] sampleData)
            {
                if (!InitMode)
                {
                    long offset = 0;
                    while (offset < sampleData.Length)
                    {
                        long time = sampleTime + offset;
                        if (time < 0)
                        {
                            offset += this.source.Allocator.Properties.BufferSize;
                            continue;
                        }

                        IMediaSample sample = this.source.GetDeliveryBuffer(TimeSpan.Zero, TimeSpan.Zero);

                        // If null, allocator is not initialized
                        if (sample == null) return;

                        sample.AddRef();
                        try
                        {
                            sample.StartTime = TimeSpan.FromTicks(time);
                            
                            sample.EndTime = sample.StartTime + TimeSpan.FromTicks(sample.Length);
                            sample.MediaType = this.source.mediaType;

                            sample.StartMediaTime = startOffset;
                            sample.EndMediaTime = sample.StartMediaTime + sample.Length;
                            
                            long c = 0;
                            for (long x = offset; x < sampleData.Length && c < sample.Length; x++)
                                sample.Data[c++] = sampleData[x];
                            sample.Length = c;

                            this.source.ProcessSample(sample);

                            offset += sample.Length;
                            startOffset += sample.Length;
                        }
                        finally
                        {
                            sample.Release();
                        }
                    }
                }
            }

        }
    }
}
